package net.p2pcdn.core.order.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qc.bean.result.Paging;
import com.qc.bean.result.ResponseData;
import net.p2pcdn.common.Date2StringConverter;
import net.p2pcdn.core.order.domain.*;
import net.p2pcdn.core.order.repository.CDNConsumeRecordRepository;
import net.p2pcdn.core.order.repository.OrderDetailRepository;
import net.p2pcdn.core.order.repository.OrderRepository;
import net.p2pcdn.core.product.domain.Product;
import net.p2pcdn.core.product.domain.ProductType;
import net.p2pcdn.core.product.domain.VirtualItem;
import net.p2pcdn.core.product.repository.VirtualItemRepository;
import net.p2pcdn.core.product.service.ProductService;
import net.p2pcdn.user.domain.User;
import net.p2pcdn.user.service.UserService;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class OrderService {
    private final static String ELEMENTS = "9837641250";
    @Resource
    private OrderRepository orderRepository;
    @Resource
    private OrderDetailRepository orderDetailRepository;
    @Autowired
    private UserService userService;
    @Autowired
    private ProductService productService;
    @Autowired
    private ContractService contractService;
    @Autowired
    private OrderHandlerFactory orderHandlerFactory;
    @Resource
    private VirtualItemRepository virtualItemRepository;
    @Resource
    private CDNConsumeRecordRepository cdnConsumeRecordRepository;

    public String generateOrderNo() {
        String date = Date2StringConverter.yyyyMMddHHmmss(Calendar.getInstance());
        String randomStr = RandomStringUtils.random(14, ELEMENTS);
        return date + randomStr;

    }

    /**
     * 管理员帮用户开通套餐，或者添加流量包
     * @param creator
     * @param email
     * @param productId
     * @param actualPayment
     * @param domains
     * @param payType
     * @param platformEnum
     * @param remark
     */
    public void openTrafficPackageService(int creator, String email, int productId, BigDecimal actualPayment,
                                 String domains, PayType payType, PlatformEnum platformEnum,
                                 String remark,int amount) {
        User user = userService.findByUsername(email);
        if (user == null) {
            throw new RuntimeException("根据邮箱未找到用户");
        }
        Product product = this.productService.getProduct(productId);
        if(product.getType() != ProductType.PACKAGE.getValue()){
            throw new RuntimeException("只能选择套餐");
        }
        String regex = "[,;，；\n]";
        ResponseData responseData = contractService.doesDomainBundled(user.getId(),productId, Stream.of(domains.split(regex)).collect(Collectors.toList()));
        if(!responseData.isSuccess()){
            throw new RuntimeException(responseData.getMessage());
        }
        BigDecimal payment = product.getPrice();
        Order order = new Order(user.getId(), generateOrderNo(), payment, actualPayment, payType, platformEnum, creator,amount);
        order.setRemark(remark);
        order.setTradeStatus(TradeStatusEnum.TRADE_SUCCESS.getValue());
        this.orderRepository.insert(order);
        OrderDetail detail = order.addItem(product, domains);
        this.orderDetailRepository.insert(detail);
        orderHandlerFactory.handle(product.getType() == ProductType.PACKAGE.getValue() ? ProductType.PACKAGE : ProductType.BAG, detail);
    }

    /**
     * 充值订单
     * @param creator
     * @param userId
     * @param money
     * @param payType
     * @param platform
     * @return
     */
    public Order addChargeOrder(int creator, int userId, BigDecimal money ,PayType payType, PlatformEnum platform) {
        Order order = new Order(userId, generateOrderNo(), money, money, payType, platform, creator,1);
        if(payType == PayType.OFFLINE && PlatformEnum.UNKNOWN == platform){
            order.setTradeStatus(TradeStatusEnum.TRADE_SUCCESS.getValue());
        }else {
            order.setTradeStatus(TradeStatusEnum.WAIT_BUYER_PAY.getValue());
        }
        this.orderRepository.insert(order);
        OrderDetail detail = new OrderDetail();
        VirtualItem virtualItem = virtualItemRepository.selectOne();
        detail.setItemId(virtualItem.getId());
        detail.setItemType(ProductType.BALANCE.getValue());
        detail.setName(virtualItem.getName());
        detail.setOrderId(order.getId());
        detail.setPrice(money);
        this.orderDetailRepository.insert(detail);
        return order;
    }


    public Order createPackageOrder(int userId, int productId, int quantity, BigDecimal money, PayType payType, PlatformEnum platform,String  domains) {
        Product product = productService.getProduct(productId);
        BigDecimal finalMoney;
        if (money == null) {
            finalMoney = product.getPrice().multiply(new BigDecimal(quantity));
        } else {
            finalMoney = money;
        }
        Order order = new Order(userId, generateOrderNo(), product.getPrice().multiply(new BigDecimal(quantity)), finalMoney, payType, platform, userId,quantity);
        order.setTradeStatus(TradeStatusEnum.WAIT_BUYER_PAY.getValue());
        this.orderRepository.insert(order);
        OrderDetail detail = new OrderDetail();
        detail.setItemId(productId);
        detail.setNum(quantity);
        detail.setItemType(product.getType());
        detail.setName(product.getName());
        detail.setOrderId(order.getId());
        detail.setPrice(money);
        detail.setDomains(domains);
        this.orderDetailRepository.insert(detail);
        return order;
    }

    public Order createTrafficOrder(int userId, int productId, int quantity, BigDecimal money, PayType payType, PlatformEnum platform,Integer contractId) {
        Product product = productService.getProduct(productId);
        BigDecimal finalMoney;
        if (money == null) {
            finalMoney = product.getPrice().multiply(new BigDecimal(quantity));
        } else {
            finalMoney = money;
        }
        Order order = new Order(userId, generateOrderNo(), product.getPrice().multiply(new BigDecimal(quantity)), finalMoney, payType, platform, userId,quantity);
        order.setTradeStatus(TradeStatusEnum.WAIT_BUYER_PAY.getValue());
        this.orderRepository.insert(order);
        OrderDetail detail = new OrderDetail();
        detail.setItemId(productId);
        detail.setItemType(product.getType());
        detail.setNum(quantity);
        detail.setName(product.getName());
        detail.setOrderId(order.getId());
        detail.setPrice(money);
        detail.setContractId(contractId);
        this.orderDetailRepository.insert(detail);
        return order;
    }

    public Paging<OrderVO> queryOrder(OrderQueryCommand command) throws ParseException {
        if (StringUtils.isBlank(command.getFromDate()) && StringUtils.isBlank(command.getToDate())) {
            command.setFrom(DateUtils.addWeeks(new Date(), -1));
            command.setTo(new Date());
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if (StringUtils.isNotBlank(command.getToDate())) {
            command.setTo(sdf.parse(command.getToDate() + " 23:59:59"));
        }

        if (StringUtils.isNotBlank(command.getFromDate())) {
            command.setFrom(sdf.parse(command.getFromDate() + " 00:00:00"));
        }
        List<OrderVO> list = this.orderRepository.query(command);
        int count = this.orderRepository.count(command);
        return new Paging<>(count, list);
    }

    public Map<String, Object> groupByDailyIncome(String from, String to) {
        List<KeyValue> kvs;
        Date end;
        Date start;
        if (StringUtils.isBlank(from) || StringUtils.isBlank(to)) {
            end = new Date();
            start = DateUtils.addWeeks(end, -1);
        } else {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            try {
                start = sdf.parse(from);
            } catch (ParseException e) {
                throw new RuntimeException("日期格式错误");
            }
            try {
                end = sdf.parse(to);
            } catch (ParseException e) {
                throw new RuntimeException("日期格式错误");
            }
        }
        Map<String, Object> params = new HashMap<>(4);
        params.put("from", start);
        params.put("to", end);
        kvs = orderRepository.groupByDailyIncome(params);
        List<String> days = Date2StringConverter.splitDateInterval(start, end);
        Map<String, Double> kvm = new TreeMap<>((Comparator.naturalOrder()));
        for (KeyValue kv : kvs) {
            kvm.put(kv.getKey(), Double.parseDouble(kv.getValue()));
        }
        days.forEach(item -> {
            if (!kvm.containsKey(item)) {
                kvm.put(item, 0D);
            }
        });
        List<String> labels = new ArrayList<>();
        List<Double> data = new ArrayList<>();
        for (Map.Entry<String, Double> entry : kvm.entrySet()) {
            labels.add(entry.getKey());
            data.add(entry.getValue());
        }
        Map<String, Object> result = new HashMap<>();
        result.put("labels", labels);
        result.put("data", data);
        return result;
    }

    public Map<String, Object> groupByMonthIncome(String from, String to) {
        List<KeyValue> kvs;
        Date end;
        Date start;
        if (StringUtils.isBlank(from) || StringUtils.isBlank(to)) {
            end = new Date();
            start = Date2StringConverter.getCurrentYearStartTime();
        } else {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            try {
                start = sdf.parse(from);
            } catch (ParseException e) {
                throw new RuntimeException("日期格式错误");
            }
            try {
                end = sdf.parse(to);
            } catch (ParseException e) {
                throw new RuntimeException("日期格式错误");
            }
        }
        Map<String, Object> params = new HashMap<>();
        params.put("from", start);
        params.put("to", end);
        kvs = orderRepository.groupByMonthIncome(params);
        List<String> months = Date2StringConverter.splitMonthInterval(start, end);
        Map<String, Double> kvm = new TreeMap<>((Comparator.naturalOrder()));
        for (KeyValue kv : kvs) {
            kvm.put(kv.getKey(), Double.parseDouble(kv.getValue()));
        }
        months.forEach(item -> {
            if (!kvm.containsKey(item)) {
                kvm.put(item, 0D);
            }
        });
        List<String> labels = new ArrayList<>();
        List<Double> data = new ArrayList<>();
        for (Map.Entry<String, Double> entry : kvm.entrySet()) {
            labels.add(entry.getKey());
            data.add(entry.getValue());
        }
        Map<String, Object> result = new HashMap<>();
        result.put("labels", labels);
        result.put("data", data);
        return result;
    }


    public OperationData getOperationData() {
        return new OperationData(orderRepository.findOperationData());
    }

    public Paging<CDNConsumeRecord> queryTrafficDetails(int page, int size, Integer contractId, String from, String to, Boolean done, String domain) throws ParseException {
        PageHelper.startPage(page, size);
        Map<String, Object> params = new HashMap<>();
        params.put("contractId", contractId);
        if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            params.put("from", sdf.parse(from));
            params.put("to", sdf.parse(to));
        }
        params.put("done", done);
        params.put("domain", domain);
        List<CDNConsumeRecord> list = cdnConsumeRecordRepository.query(params);
        PageInfo<CDNConsumeRecord> pageInfo = new PageInfo<>(list);
        return new Paging<>((int) pageInfo.getTotal(), pageInfo.getList());
    }

    public OrderDetail getOrderDetail(int orderId) {
        return  orderDetailRepository.selectByOrderId(orderId);
    }

    public Order findOrderByTradeNo(String orderNo) {
        return orderRepository.findByOrderNo(orderNo);
    }

    public void save(Order order) {
        this.orderRepository.updateByPrimaryKey(order);
    }

    public void cancelOrder(String orderNo){
        Order order = this.orderRepository.findByOrderNo(orderNo);
        if(order != null){
            order.setTradeStatus(TradeStatusEnum.CANCEL.getValue());
            this.save(order);
        }
    }
}
